package com.gelingqin.es.springbootdemo.util;

import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

/**
 * String工具类
 * 
 * @author zhengzeng
 * @date 2018/07/13
 */

public class StringHelper {

  public static final String[] EMPTY_STRING_ARRAY = new String[0];
  public static final String AMPERSAND = "&";
  public static final String AND = "and";
  public static final String AT = "@";
  public static final String ASTERISK = "*";
  public static final String DOUBLE_ASTERISK = "**";
  public static final String STAR = ASTERISK;
  public static final String BACK_SLASH = "\\";
  public static final String COLON = ":";
  public static final String COLON_SLASH = ":/";
  public static final String COMMA = ",";
  public static final String DASH = "-";
  public static final String DOLLAR = "$";
  public static final String DOT = ".";
  public static final String DOTDOT = "..";
  public static final String DOT_CLASS = ".class";
  public static final String DOT_JAVA = ".java";
  public static final String EMPTY = "";
  public static final String EQUALS = "=";
  public static final String FALSE = "false";
  public static final String SLASH = "/";
  public static final String BACKSLASH = "\\";
  public static final String HASH = "#";
  public static final String HAT = "^";
  public static final String LEFT_BRACE = "{";
  public static final String LEFT_BRACKET = "(";
  public static final String LEFT_CHEV = "<";
  public static final String NEWLINE = "\n";
  public static final String N = "n";
  public static final String NO = "no";
  public static final String NULL = "null";
  public static final String OFF = "off";
  public static final String ON = "on";
  public static final String PERCENT = "%";
  public static final String PIPE = "|";
  public static final String PLUS = "+";
  public static final String QUESTION_MARK = "?";
  public static final String EXCLAMATION_MARK = "!";
  public static final String QUOTE = "\"";
  public static final String RETURN = "\r";
  public static final String TAB = "\t";
  public static final String RIGHT_BRACE = "}";
  public static final String RIGHT_BRACKET = ")";
  public static final String RIGHT_CHEV = ">";
  public static final String SEMICOLON = ";";
  public static final String SINGLE_QUOTE = "'";
  public static final String BACKTICK = "`";
  public static final String SPACE = " ";
  public static final String TILDA = "~";
  public static final String LEFT_SQ_BRACKET = "[";
  public static final String RIGHT_SQ_BRACKET = "]";
  public static final String TRUE = "true";
  public static final String UNDERSCORE = "_";
  public static final String UTF_8 = "UTF-8";
  public static final String US_ASCII = "US-ASCII";
  public static final String ISO_8859_1 = "ISO-8859-1";
  public static final String Y = "y";
  public static final String YES = "yes";
  public static final String ONE = "1";
  public static final String ZERO = "0";
  public static final String DOLLAR_LEFT_BRACE = "${";
  public static final String CRLF = "\r\n";
  public static final String GET = "get";
  public static final String SET = "set";
  public static final String HTML_NBSP = "&nbsp;";
  public static final String HTML_AMP = "&amp";
  public static final String HTML_QUOTE = "&quot;";
  public static final String HTML_LT = "&lt;";
  public static final String HTML_GT = "&gt;";
  public static final String MIME_APPLICATION_XML = "application/xml";
  public static final String MIME_APPLICATION_JSON = "application/json";
  public static final String MIME_TEXT_HTML = "text/html";
  public static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream";
  public static final String[] EMPTY_ARRAY = new String[0];

  public static final String POST = "POST";
  public static final String OS_LINUX = "linux";
  public static final String OS_WINDOW = "window";
  public static final String OS_MAC = "mac";
  public static final String OS_SOLARIS = "solaris";
  public static final String OS_HP = "hp";
  public static final String OS_AIX = "aix";
  public static final byte[] BYTES_NEW_LINE = "\n".getBytes();
  public static final Charset UTF8 = Charset.forName("UTF-8");

  /**
   * 字符串是否为空，空的定义如下:<br>
   * 1、为null <br>
   * 2、为""<br>
   * 
   * @param str 被检测的字符串
   * @return 是否为空
   */
  public static boolean isEmpty(CharSequence str) {
    return str == null || str.length() == 0;
  }

  /**
   * 字符串是否不为空，空的定义如下返回为true:<br>
   * 1、为null <br>
   * 2、为""<br>
   * 
   * @param str
   * @return
   */
  public static boolean isNotEmpty(CharSequence str) {
    return !isEmpty(str);
  }

  public static boolean isNotBlank(CharSequence str) {
    return !isBlank(str);
  }

  /**
   * 字符串是否为空白 空白的定义如下： <br>
   * 1、为null <br>
   * 2、为不可见字符（如空格、全角空格）<br>
   * 3、""<br>
   * 
   * @param str 被检测的字符串
   * @return 是否为空
   */
  public static boolean isBlank(CharSequence str) {
    int length;

    if ((str == null) || ((length = str.length()) == 0)) {
      return true;
    }

    for (int i = 0; i < length; i++) {
      // 只要有一个非空字符即为非空字符串
      char c = str.charAt(i);
      boolean isBlankChar =
          Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
      if (!isBlankChar) {
        return false;
      }
    }

    return true;
  }

  /**
   * 驼峰格式化 将首字母小写
   * 
   * @return
   */
  public static String camel(String name) {

    return name.substring(0, 1).toLowerCase() + name.substring(1);
  }

  /**
   * 字符串按分隔符分隔成数组
   * 
   * @param arrString 要进行分隔的字符串
   * @param separator 分隔符
   * @return 数组，如果为null或"" 返回空数组
   */
  public static String[] split(String arrString, char separator) {

    List<String> list = splitList(arrString, separator);

    return (String[]) list.toArray(EMPTY_STRING_ARRAY);
  }

  public static String[] trim(String[] arr) {
    if (arr == null) {
      return null;
    }
    for (int i = 0; i < arr.length; i++) {
      if (arr[i] != null) {
        arr[i] = arr[i].trim();
      }
    }
    return arr;
  }

  public static String trim(String message) {
    if (message == null) {
      return null;
    } else {
      return message.trim();
    }
  }

  public static String repeat(final String source, int count) {
    StringBuilder result = new StringBuilder(count);
    while (count > 0) {
      result.append(source);
      count--;
    }
    return result.toString();
  }

  public static String repeat(final char c, final int count) {
    char[] result = new char[count];
    for (int i = 0; i < count; i++) {
      result[i] = c;
    }
    return new String(result);
  }

  /**
   * 字符串按逗号分隔符分隔成列表
   * 
   * @param arrString 要进行分隔的字符串
   * @return 列表，如果arrString为null或""，返回空数组
   */
  public static List<String> splitList(String arrString) {
    return splitList(arrString, ',');
  }

  /**
   * 字符串按分隔符分隔成列表
   * 
   * @param arrString 要进行分隔的字符串
   * @param separator 分隔符
   * @return 列表，如果arrString为null或""，返回空数组
   */
  public static List<String> splitList(String arrString, char separator) {

    List<String> list = new ArrayList<String>();
    // 如果arrString为null或""，返回空数组
    if (null == arrString || arrString.equals("")) {
      return list;
    }
    char ch;
    int index = 0, length = arrString.length();
    for (int i = 0; i < length; i++) {
      ch = arrString.charAt(i);
      if (ch == separator) {
        list.add(arrString.substring(index, i));
        index = i + 1;
      }
    }
    // index==0 没有找到，只有一个元素
    if (index == 0) {
      list.add(arrString);
      // 最后一个元素
    } else if (index <= length - 1) {
      list.add(arrString.substring(index));

    }

    return list;
  }

  /**
   * 获取父路路径
   * 
   * @param path
   * @return
   */
  public static String getParent(String path) {

    path = normalPath(path);

    // 如果path为null，或是根目录 ,返回null
    if (path == null || path.equals(SLASH) || path.endsWith(COLON_SLASH)) {
      return null;
    }

    int last = path.lastIndexOf("/", path.length() - 1);

    String parentPath = path.substring(0, last);

    return parentPath;
  }

  /**
   * 路径标准化,统一window下\为/,并去重/\与空格
   * 
   * @param path
   * @return
   */
  public static String normalPath(String path) {

    if (path == null) {
      return null;
    }

    path = path.trim();

    StringBuilder sbd = new StringBuilder();
    char lastChar = ' ';
    for (int i = 0; i < path.length(); i++) {
      char ch = path.charAt(i);
      boolean isPathChar = (ch == '/' || ch == '\\');
      if (isPathChar && lastChar == '/') {
        continue;
      } else if (isPathChar && lastChar != '/') {
        sbd.append('/');
        lastChar = '/';
      } else {
        sbd.append(ch);
        lastChar = ch;
      }
    }
    return sbd.toString().trim();
  }

  /**
   * 路径正常化,使window或是linux下一致化 dir后会会增加/
   * 
   * @param dir
   * @return
   */
  public static String normalDir(String dir) {
    String path = normalPath(dir);
    if (!path.endsWith(SLASH)) {
      return path = path + "/";
    } else {
      return path;
    }
  }

  /**
   * 根据文件的全路径获取 文件名称
   * 
   * @param filePath
   * @return
   */
  public static String getFileName(String filePath) {

    filePath = normalPath(filePath);
    int lastIndex = filePath.lastIndexOf("/");
    if (lastIndex > 0) {
      return filePath.substring(lastIndex + 1);
    } else {
      return filePath;
    }
  }

  public static String getFileExtension(String filePath) {
    String fileName = getFileName(filePath);
    if (fileName == null) {
      return null;
    }
    int index = fileName.lastIndexOf(".");
    if (index == -1) {
      return EMPTY;
    } else {
      return fileName.substring(index + 1);
    }
  }





  public static final char C_BACKSLASH = '\\';
  public static final char C_DELIM_START = '{';
  public static final String EMPTY_JSON = "{}";

  /**
   * 格式化文本, {} 表示占位符<br>
   * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
   * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
   * 例：<br>
   * 通常使用：format("this is {} for {}", "a", "b") =》 this is a for b<br>
   * 转义{}： format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
   * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
   * 
   * @param template 文本模板，被替换的部分用 {} 表示
   * @param params 参数值
   * @return 格式化后的文本
   */
  public static String format(String strPattern, Object... argArray) {
    if (null == strPattern) {
      return null;
    }
    if (isBlank(strPattern) || null == argArray) {
      return strPattern;
    }
    final int strPatternLength = strPattern.length();

    // 初始化定义好的长度以获得更好的性能
    StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
    // 记录已经处理到的位置
    int handledPosition = 0;
    // 占位符所在位置
    int delimIndex;
    for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
      delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
      // 剩余部分无占位符
      if (delimIndex == -1) {
        // 不带占位符的模板直接返回
        if (handledPosition == 0) {
          return strPattern;
          // 字符串模板剩余部分不再包含占位符，加入剩余部分后返回结果
        } else {
          sbuf.append(strPattern, handledPosition, strPatternLength);
          return sbuf.toString();
        }
      } else {
        // 转义符
        if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
          // 双转义符
          if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
            // 转义符之前还有一个转义符，占位符依旧有效
            sbuf.append(strPattern, handledPosition, delimIndex - 1);
            sbuf.append(toString(argArray[argIndex]));
            handledPosition = delimIndex + 2;
          } else {
            // 占位符被转义
            argIndex--;
            sbuf.append(strPattern, handledPosition, delimIndex - 1);
            sbuf.append(C_DELIM_START);
            handledPosition = delimIndex + 1;
          }
        } else {// 正常占位符
          sbuf.append(strPattern, handledPosition, delimIndex);
          sbuf.append(toString(argArray[argIndex]));
          handledPosition = delimIndex + 2;
        }
      }
    }
    // append the characters following the last {} pair.
    // 加入最后一个占位符后所有的字符
    sbuf.append(strPattern, handledPosition, strPattern.length());

    return sbuf.toString();
  }

  private static Pattern VARIABLE_PATTERN = Pattern
      .compile("\\s*\\{\\s*([\\._0-9a-zA-Z=]+)\\s*\\}");



  /**
   * 格式化异常信息
   * 
   * @param throwable
   * @return
   */
  public static String format(Throwable throwable) {

    StringBuilder sbuf = new StringBuilder();
    if (throwable == null) {
      return null;
    }
    sbuf.append(throwable.getClass().getName()).append(" ").append(throwable.getMessage())
        .append("\r\n");
    StackTraceElement[] stackTraceElements = throwable.getStackTrace();
    for (StackTraceElement stackTraceElement : stackTraceElements) {
      sbuf.append("\tat ");
      if (stackTraceElement.isNativeMethod()) {
        sbuf.append(stackTraceElement.getClassName()).append(".")
            .append(stackTraceElement.getMethodName()).append("(native method)");
      } else {
        sbuf.append(stackTraceElement.getClassName()).append(".")
            .append(stackTraceElement.getMethodName()).append("(")
            .append(stackTraceElement.getLineNumber()).append(")");

      }
      sbuf.append("\r\n");
    }

    if (throwable.getCause() != null) {
      sbuf.append("Caused by: ").append(format(throwable.getCause()));
    }

    return sbuf.toString();
  }

  private static final char[] INT_TO_BASE64 = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
      'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
      'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
      'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};

  /**
   * 格式化成base64字符串
   * 
   * @param content
   * @return
   */
  public static String formatBase64(byte[] content) {

    int aLen = content.length;
    int numFullGroups = aLen / 3;
    int numBytesInPartialGroup = aLen - (3 * numFullGroups);
    int resultLen = 4 * (aLen + 2) / 3;
    StringBuffer result = new StringBuffer(resultLen);
    char[] intToAlpha = INT_TO_BASE64;

    int inCursor = 0;
    for (int i = 0; i < numFullGroups; ++i) {
      int byte0 = content[(inCursor++)] & 0xFF;
      int byte1 = content[(inCursor++)] & 0xFF;
      int byte2 = content[(inCursor++)] & 0xFF;
      result.append(intToAlpha[(byte0 >> 2)]);
      result.append(intToAlpha[(byte0 << 4 & 0x3F | byte1 >> 4)]);
      result.append(intToAlpha[(byte1 << 2 & 0x3F | byte2 >> 6)]);
      result.append(intToAlpha[(byte2 & 0x3F)]);
    }

    if (numBytesInPartialGroup != 0) {
      int byte0 = content[(inCursor++)] & 0xFF;
      result.append(intToAlpha[(byte0 >> 2)]);
      if (numBytesInPartialGroup == 1) {
        result.append(intToAlpha[(byte0 << 4 & 0x3F)]);
        result.append("==");
      } else {
        int byte1 = content[(inCursor++)] & 0xFF;
        result.append(intToAlpha[(byte0 << 4 & 0x3F | byte1 >> 4)]);
        result.append(intToAlpha[(byte1 << 2 & 0x3F)]);
        result.append('=');
      }

    }

    return result.toString();

  }

  public static String formatBase64(String content) {
    return formatBase64(content.getBytes(UTF8));
  }

  private static final byte[] BASE64_TO_INT = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
      -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
      20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
      37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};

  /**
   * 解析base64为字节
   * 
   * @param base64
   * @return
   */
  public static byte[] parseBase64(String base64) {

    int sLen = base64.length();
    int numGroups = sLen / 4;
    if (4 * numGroups != sLen) {
      throw new IllegalArgumentException("String length must be a multiple of four.");
    }
    int missingBytesInLastGroup = 0;
    int numFullGroups = numGroups;
    if (sLen != 0) {
      if (base64.charAt(sLen - 1) == '=') {
        ++missingBytesInLastGroup;
        --numFullGroups;
      }
      if (base64.charAt(sLen - 2) == '=') {
        ++missingBytesInLastGroup;
      }
    }
    byte[] result = new byte[3 * numGroups - missingBytesInLastGroup];

    int inCursor = 0;
    int outCursor = 0;
    for (int i = 0; i < numFullGroups; ++i) {
      int ch0 = BASE64_TO_INT[base64.charAt(inCursor++)];
      int ch1 = BASE64_TO_INT[base64.charAt(inCursor++)];
      int ch2 = BASE64_TO_INT[base64.charAt(inCursor++)];
      int ch3 = BASE64_TO_INT[base64.charAt(inCursor++)];
      result[(outCursor++)] = (byte) (ch0 << 2 | ch1 >> 4);
      result[(outCursor++)] = (byte) (ch1 << 4 | ch2 >> 2);
      result[(outCursor++)] = (byte) (ch2 << 6 | ch3);
    }

    if (missingBytesInLastGroup != 0) {
      int ch0 = BASE64_TO_INT[base64.charAt(inCursor++)];
      int ch1 = BASE64_TO_INT[base64.charAt(inCursor++)];
      result[(outCursor++)] = (byte) (ch0 << 2 | ch1 >> 4);

      if (missingBytesInLastGroup == 1) {
        int ch2 = BASE64_TO_INT[base64.charAt(inCursor++)];
        result[(outCursor++)] = (byte) (ch1 << 4 | ch2 >> 2);
      }

    }

    return result;

  }

  public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens,
      boolean ignoreEmptyTokens) {
    if (str == null) {
      return null;
    }
    StringTokenizer st = new StringTokenizer(str, delimiters);
    List<String> tokens = new ArrayList<String>();
    while (st.hasMoreTokens()) {
      String token = st.nextToken();
      if (trimTokens) {
        token = token.trim();
      }
      if ((!(ignoreEmptyTokens)) || (token.length() > 0)) {
        tokens.add(token);
      }
    }
    return ((String[]) (String[]) tokens.toArray(new String[tokens.size()]));

  }


  /**
   * 将对象转为字符串<br>
   * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
   * 
   * @param obj 对象
   * @param charset 字符集
   * @return 字符串
   */
  public static String toString(Object obj) {
    if (null == obj) {
      return null;
    }

    return obj.toString();
  }

  /**
   * 解析double字符串 当value为null或"",返回0,
   * 
   * @param value
   * @return
   */
  public static double parseDouble(String value) {
    if (isEmpty(value)) {
      return 0;
    } else {
      return Double.parseDouble(value);
    }
  }

  /**
   * 解析double字符串 当value为null或"",返回0,
   * 
   * @param value
   * @return
   */
  public static double parseDouble(Object value) {
    if (value == null) {
      return 0;
    } else {
      return parseDouble(value.toString());
    }

  }
  /**
   * 格式化double保留2位小数
   * @param d
   * @return
   */
  public static String formatDouble(double d){
    DecimalFormat df = new DecimalFormat("#.00");
    String str = df.format(d);
    return str;
  }

  /**
   * 解析long字符串 当value为null或"",返回0,
   * 
   * @param value
   * @return
   */
  public static long parseLong(String value) {
    if (isEmpty(value)) {
      return 0;
    } else {
      return Long.parseLong(value);
    }
  }

  /**
   * 解析long字符串 当value为null或"",返回0,
   * 
   * @param value
   * @return
   */
  public static long parseLong(Object value) {
    if (value == null) {
      return 0;
    } else {
      return parseLong(value.toString());
    }
  }


  /**
   * 连接数组
   * 
   * @param array
   * @param str
   * @return
   */
  public static String join(String[] array, CharSequence str) {
    if (array == null || array.length == 0) return "";
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < array.length; ++i) {
      if (i > 0) sb.append(str);
      sb.append(array[i]);
    }
    return sb.toString();
  }

  /**
   * 连接集合
   * 
   * @param list
   * @param str
   * @return
   */
  public static String join(Collection<String> list, CharSequence str) {
    if (list == null || list.size() == 0) return "";
    StringBuilder sb = new StringBuilder();
    int i = 0;
    for (String msg : list) {
      if (i > 0) sb.append(str);
      sb.append(msg);
      i++;
    }
    return sb.toString();
  }

  /**
   * 获取 className的 simpleName
   * 
   * @param className
   * @return
   */
  public static String getSimpleName(String className) {
    int index = className.lastIndexOf('.');
    return className.substring(index + 1);
  }

  /**
   * 工具类，不可new
   */
  private StringHelper() {

  }

}
